package com.tangyuan.userauth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.tangyuan.common.constant.RedisConstant;
import com.tangyuan.common.convert.address.AddressConvert;
import com.tangyuan.common.result.Result;
import com.tangyuan.common.result.ResultCodeEnum;
import com.tangyuan.common.util.*;
import com.tangyuan.dto.*;
import com.tangyuan.feignapis.live.ILiveRoomFeignClient;
import com.tangyuan.model.User;
import com.tangyuan.model.UserForget;
import com.tangyuan.model.UserLogin;
import com.tangyuan.userauth.mapper.UserMapper;
import com.tangyuan.userauth.service.UserLikeService;
import com.tangyuan.userauth.service.UserService;
import com.tangyuan.vo.LoginVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;
    @Autowired
    private UserLikeService userLikeService;
    @Autowired
    ILiveRoomFeignClient liveRoomFeignClient;

    @Autowired
    RedisTemplate redisTemplate;

    @Value("${spring.profiles.active}")
    private String env;

    @Value("${agent.downlines:}")
    private String agentDownlinesUrl;

    private static final AddressConvert addressConvert = AddressConvert.INSTANCE;

    @Override
    @Transactional
    public Result userRegis(UserRegistDto userRegistDto) {
        boolean phoneNumber = PhoneNumberUtil.isValidPhoneNumber(userRegistDto.getMobile());
        if (!phoneNumber) {
            return Result.build(null,ResultCodeEnum.INCORRECT_PHONE_FORMAT);
        }
        // 手机号重复验证
        User user = userMapper.getUserByPhoneNumber(userRegistDto.getMobile());
        if (user != null) {
            return Result.build(null, ResultCodeEnum.USER_EXIST);
        }
        // 先看缓存里有没有没过期的code
        // 定义一个RedisKey
        String registCodeprefix = RedisConstant.USER_REGIST_KEY_PREFIX + userRegistDto.getMobile();
        // 查有没有code
        Object registCode = redisTemplate.opsForValue().get(registCodeprefix);
        if (!"dev".equalsIgnoreCase(env)) {
            if (registCode == null) {
                return Result.build(null, ResultCodeEnum.CODE_OVERTIME);
            }
            if (!registCode.toString().equals(userRegistDto.getRegistCode())) {
                return Result.build(null, ResultCodeEnum.PHONE_CODE_ERROR);
            }
        }
        // 验证码正确 注册新用户
        User userRegis = BeanUtil.copyProperties(userRegistDto, User.class);
        // 生成一个随机数用作用户id
        SnowUtil snowUtil = new SnowUtil();
        String randomNumber = String.valueOf(snowUtil.generateId()).substring(0, 8);
        // 设置时间
        userRegis.setCreateTime(new Date());
        // 设置用户id
        userRegis.setAccount(randomNumber);
        // 密码加密
        userRegis.setPassword(MD5.encrypt(userRegis.getPassword()));
        // 添加用户默认昵称
        userRegis.setName("唐园" + RandomUtil.randomNumbers(6));
        // 添加用户默认头像
        userRegis.setHeadImage("https://shengtangfeng1.oss-cn-qingdao.aliyuncs.com/head_image.png");
        // 添加用户
        userMapper.addUser(userRegis);

        // 请求后台管理系统添加下线功能
        if (userRegistDto.getAreaId() != null) {
            String areaNameList = addressConvert.convertAreaIdToAreaName(userRegistDto.getAreaId().intValue());
            log.warn("打印传过来的街道信息：{}", areaNameList);
            String[] parts = areaNameList.split(" ");
            // 获取最后一个元素
            String province = parts[0];
            String city = parts[1];
            String country = parts[2];
            String street = parts[3];
            // 把解析出来地区信息，插入User表一份
            userRegis.setProvince(province);
            userRegis.setCity(city);
            userRegis.setCountry(country);
            userRegis.setStreet(street);

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("street", street);
            jsonObject.put("downlineId", userRegis.getId());
            jsonObject.put("platform", "唐园视频");
            jsonObject.put("taskType", "0");

            Object response = HttpUtil.doPost(agentDownlinesUrl, jsonObject);
            log.warn("查看远程调用后台添加下线接口返回信息:{} ", response);
        }

        //插入用户喜好默认值0.00
        userLikeService.addUser(userRegis.getId());
        redisTemplate.delete(registCodeprefix);
        // 生成用户token
        String token = IdUtil.fastSimpleUUID();
        String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
        String onlineKey = RedisConstant.USER_ONLINE_KEY_PREFIX + userRegistDto.getMobile();
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        // 登录用户token存入在线用户
        redisTemplate.opsForValue().set(onlineKey, token, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
        // 将用户信息存入redis中
        redisTemplate.opsForValue().set(loginKey, userRegis, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
        redisTemplate.exec();
        userRegis.setToken(token);
        userRegis.setIsFirstLogin(1);
        return Result.ok(userRegis);
    }

    @Override
    @Transactional
    public Result userPwdLogin(UserPwdLoginDto userPwdLoginDto) {
        // 根据电话号码查询用户信息
        User user = userMapper.getUserByPhoneNumber(userPwdLoginDto.getMobile());
        if (user == null) {
            return Result.build(null, ResultCodeEnum.USER_NOT_EXIST);
        }
        if (user.getPassword() == null) {
            return Result.build(null, ResultCodeEnum.PASSWORD_ERROR);
        }
        if (!MD5.encrypt(userPwdLoginDto.getPassword()).equals(user.getPassword())) {
            return Result.build(null, ResultCodeEnum.PASSWORD_ERROR);
        }
        return Result.ok(getLoginVo(userPwdLoginDto.getMobile(), user, null));
    }


    @Override
    public Result getCodeLogin(String mobile) {
        // 手机号是否存在
        User user = userMapper.getUserByPhoneNumber(mobile);
        if (user == null) {
            return Result.build(null, ResultCodeEnum.USER_NOT_EXIST);
        }
        String loginCodePrefix = RedisConstant.USER_LOGIN_CODE + mobile;
        redisTemplate.delete(loginCodePrefix);
        // 随机生成验证码
        // 生成一个6位数字验证码
        String code = RandomCodeUtil.getRandomCode();
        code = smsSendMessage(mobile, code);
        // 得到正确验证码先把验证码存在缓存里
        // 设置验证码过期时间为5分钟
        redisTemplate.opsForValue().set(loginCodePrefix, code, RedisConstant.USER_CODE_TIMEOUT, TimeUnit.SECONDS);
        return Result.ok();
    }

    @Override
    public Result userGetCodeRegis(String mobile) {
        // 手机号重复验证
        User user = userMapper.getUserByPhoneNumber(mobile);
        if (user != null) {
            return Result.build(null, ResultCodeEnum.USER_EXIST);
        }

        String loginCodePrefix = RedisConstant.USER_REGIST_KEY_PREFIX + mobile;
        // 短信防刷
        String verificationCode = (String) redisTemplate.opsForValue().get(loginCodePrefix);
        if (StringUtils.isNotBlank(verificationCode)) {
            return Result.build(null, ResultCodeEnum.REPEAT_SUBMIT);
        }
        redisTemplate.delete(loginCodePrefix);
        // 随机生成验证码
        // 生成一个6位数字验证码
        String code = RandomCodeUtil.getRandomCode();
        code = smsSendMessage(mobile, code);
        // 得到正确验证码先把验证码存在缓存里
        log.info("验证码为：{}", code);
        // 设置验证码过期时间为5分钟
        redisTemplate.opsForValue().set(loginCodePrefix, code, RedisConstant.USER_CODE_TIMEOUT, TimeUnit.SECONDS);
        return Result.ok();
    }

    @Override
    public Result userGetCodeResetPwd(String mobile) {
        User user = userMapper.getUserByPhoneNumber(mobile);
        if (user == null) {
            return Result.build(null, ResultCodeEnum.USER_NOT_EXIST);
        }
        String loginCodePrefix = RedisConstant.USER_FORGET_KEY_PREFIX + mobile;
        redisTemplate.delete(loginCodePrefix);
        // 随机生成验证码
        // 生成一个6位数字验证码
        String code = RandomCodeUtil.getRandomCode();
        code = smsSendMessage(mobile, code);
        // 得到正确验证码先把验证码存在缓存里
        // 设置验证码过期时间为5分钟
        redisTemplate.opsForValue().set(loginCodePrefix, code, RedisConstant.USER_CODE_TIMEOUT, TimeUnit.SECONDS);
        return Result.ok();
    }

    @Override
    public Result userWXLoginGetCode(String mobile) {
        String codePrefix = RedisConstant.USER_WX_LOGIN_CODE + mobile;
        redisTemplate.delete(codePrefix);
        // 随机生成验证码
        // 生成一个6位数字验证码
        String code = RandomCodeUtil.getRandomCode();
        code = smsSendMessage(mobile, code);
        // 得到正确验证码先把验证码存在缓存里
        // 设置验证码过期时间为5分钟
        redisTemplate.opsForValue().set(codePrefix, code, RedisConstant.USER_CODE_TIMEOUT, TimeUnit.SECONDS);
        return Result.ok();
    }

    @Override
    public Result userWXLogin(String openId) {
        // 查询用户信息表 看是否有手机已经绑定
        // String openid = WechatUtils.getToken(openId).getOpenid();
        User userLogin = userMapper.getUserByOpenId(openId);
        // 没有进行过微信登录 返回空
        if (userLogin == null) {
            return Result.ok();
        }
        String mobile = userLogin.getMobile();
        User user = userMapper.getUserByPhoneNumber(mobile);
        return Result.ok(getLoginVo(mobile, user, 0));
    }

    public Result userBindMobile(UserLoginDto userLoginDto, int platform) {
        String codePrefix;
        if (platform == 0) {
            codePrefix = RedisConstant.USER_WX_LOGIN_CODE + userLoginDto.getMobile();
        } else {
            codePrefix = RedisConstant.USER_IOS_LOGIN_CODE + userLoginDto.getMobile();
        }
        // 验证验证码是否正确
        String code = (String) redisTemplate.opsForValue().get(codePrefix);
        if (code == null) {
            return Result.build(null, ResultCodeEnum.CODE_OVERTIME);
        }
        if (!code.equals(userLoginDto.getCode())) {
            return Result.build(null, ResultCodeEnum.PHONE_CODE_ERROR);
        }
        // 验证手机是否存在
        User user = userMapper.getUserByPhoneNumber(userLoginDto.getMobile());
        // 手机号码已经存在 绑定已有手机号
        if (user != null) {
            // 判断手机号是否已绑定其他三方ID，预防一个ID绑定多个手机号问题，导致登录异常 2024/5/6 下午1:37 By Wesin
            if (platform == 0 && user.getOpenId() != null) {
                return Result.build(null, ResultCodeEnum.CURRENT_PHONE_EXIST);
            }
            if (platform == 1 && user.getIosId() != null) {
                return Result.build(null, ResultCodeEnum.CURRENT_PHONE_EXIST);
            }
            if (platform == 0) {
                user.setOpenId(userLoginDto.getOpenId());
            } else if (platform == 1) {
                user.setIosId(userLoginDto.getOpenId());
            }
            redisTemplate.delete(codePrefix);
            return Result.ok(getLoginVo(userLoginDto.getMobile(), user, platform));
        }
        // 判断三方ID是否已绑定过别的手机号 2024/5/6 下午3:52 By Wesin
        if (platform == 0) {
            User openIdUser = userMapper.getUserByOpenId(userLoginDto.getOpenId());
            if (openIdUser != null) {
                return Result.build(null, ResultCodeEnum.CURRENT_OPENID_EXIST);
            }
        }
        if (platform == 1) {
            User iosIdUser = userMapper.getUserByIosId(userLoginDto.getOpenId());
            if (iosIdUser != null) {
                return Result.build(null, ResultCodeEnum.CURRENT_IOS_ID_EXIST);
            }
        }

        // 手机号码不存在 新建用户 绑定新手机号
        SnowUtil snowUtil = new SnowUtil();
        String account = String.valueOf(snowUtil.generateId()).substring(0, 8);
        String mobile = userLoginDto.getMobile();
        String name = "唐园" + RandomUtil.randomNumbers(6);
        User newUser = new User();
        newUser.setAccount(account);
        newUser.setMobile(mobile);
        newUser.setName(name);
        // 目前只有两个平台，微信openId、IOS是iosId 2024/5/6 下午1:31 By Wesin
        if (platform == 0) {
            newUser.setOpenId(userLoginDto.getOpenId());
        } else {
            newUser.setIosId(userLoginDto.getOpenId());
        }
        newUser.setIsFirstLogin(1);
        newUser.setCreateTime(DateUtil.changeLocalDateTimeToDate(LocalDateTime.now()));
        // 生成新用户存入用户表
        userMapper.setNewUser(newUser);
        redisTemplate.delete(codePrefix);
        return Result.ok(getLoginVo(mobile, newUser, platform));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result userWXLoginBindMobile(UserLoginDto userLoginDto) {
        return userBindMobile(userLoginDto, 0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result userIOSLogin(String jwt) {
        // 解码后的消息体
        JSONObject payloadObj = IOSToeknUtils.parserIdentityToken(jwt);
        // 获取消息体中的用户唯一标识
        String sub = (String) payloadObj.get("sub");
        User user = userMapper.getUserByIosId(sub);
        if (user == null) {
            Map<String, String> result = new HashMap<>();
            result.put("openId", sub);
            return Result.ok(result);
        }
        // 绑定过 直接登录成功
        return Result.ok(getLoginVo(user.getMobile(), user, 1));
    }

    @Override
    public Result userIOSBindGetCode(String mobile) {
        String codePrefix = RedisConstant.USER_IOS_LOGIN_CODE + mobile;
        redisTemplate.delete(codePrefix);
        // 随机生成验证码
        // 生成一个6位数字验证码
        String code = RandomCodeUtil.getRandomCode();
        code = smsSendMessage(mobile, code);
        // 得到正确验证码先把验证码存在缓存里
        // 设置验证码过期时间为5分钟
        redisTemplate.opsForValue().set(codePrefix, code, RedisConstant.USER_CODE_TIMEOUT, TimeUnit.SECONDS);
        return Result.ok();
    }

    private String smsSendMessage(String mobile, String code) {
        if ("dev".equalsIgnoreCase(env)) {
            code = "123456";
        } else {
            SmsUtil.sendMessage("盛唐风", "SMS_199300330", mobile, code);
        }
        return code;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result userIOSBind(UserLoginDto userLoginDto) {
        return userBindMobile(userLoginDto, 1);
    }

    @Override
    public Result userIosWXLogin(String openId) {
        openId = WechatUtils.getToken(openId).getOpenid();
        User userLogin = userMapper.getUserByOpenId(openId);
        // 没有进行过微信登录 返回空
        if (userLogin == null) {
            Map<String, String> result = new HashMap<>();
            result.put("openId", openId);
            return Result.ok(result);
        }
        String mobile = userLogin.getMobile();
        User user = userMapper.getUserByPhoneNumber(mobile);
        return Result.ok(getLoginVo(mobile, user, 0));
    }

    @Override
    public Result userIosWXLoginBindMobile(UserLoginDto userLoginDto) {
        // String openid = WechatUtils.getToken(userLoginDto.getOpenId()).getOpenid();
        // userLoginDto.setOpenId(openid);
        return userWXLoginBindMobile(userLoginDto);
    }

    private void loginRecord(User user, Integer platform) {
        // 不是三方平台不记录 2024/5/6 下午2:11 By Wesin
        if (platform == null) {
            return;
        }
        Long newUserId = user.getId();
        // 存入第三方平台记录表
        UserLogin userLogin = new UserLogin();
        userLogin.setUserId(newUserId);
        userLogin.setPlatform(platform);
        if (platform == 0) {
            userLogin.setOpenId(user.getOpenId());
        } else if (platform == 1) {
            userLogin.setOpenId(user.getIosId());
        }
        userLogin.setMobile(user.getMobile());
        userLogin.setCreateTime(DateUtil.changeLocalDateTimeToDate(LocalDateTime.now()));
        userLogin.setIsDelete(0);
        userMapper.setNewUserLogin(userLogin);
    }

    @Override
    public Result userCodeLogin(UserCodeLoginDto userCodeLoginDto) {
        // 查看当前用户是否存在
        User user = userMapper.getUserByPhoneNumber(userCodeLoginDto.getMobile());
        // 用户不存在
        if (user == null) {
            return Result.build(null, ResultCodeEnum.USER_NOT_EXIST);
        }
        // code的redisKey
        String loginCodePrefix = RedisConstant.USER_LOGIN_CODE + userCodeLoginDto.getMobile();
        String code = (String) redisTemplate.opsForValue().get(loginCodePrefix);
        if (code == null) {
            return Result.build(null, ResultCodeEnum.CODE_OVERTIME);
        }

        if (!code.equals(userCodeLoginDto.getCode())) {
            return Result.build(null, ResultCodeEnum.PHONE_CODE_ERROR);
        }
        // code正确 登录成功
        return Result.ok(getLoginVo(userCodeLoginDto.getMobile(), user, null));
    }

    private LoginVo getLoginVo(String mobile, User user, Integer platform) {
        if(user.getIsCreateIm() == null || user.getIsCreateIm() == 1){
            Result<ResultCodeEnum> imUser = liveRoomFeignClient.createIMUser(user);
            if(imUser.isOk()){
                user.setIsCreateIm(0);
            }else {
                log.error("创建IM账号失败,userId:{}",user.getId());
            }
        }


        String token = IdUtil.fastSimpleUUID();
        String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
        String onlineKey = RedisConstant.USER_ONLINE_KEY_PREFIX + mobile;
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.multi();
        // 根据用户名查询token是否存在，存在则删除原token，做到登录唯一
        String onlineToken = (String) redisTemplate.opsForValue().get(onlineKey);
        if (StringUtils.isNotBlank(onlineToken)) {
            // 在线token存在，说明已有用户登录，根据已登录token查询用户 2024/4/24 下午12:05 By Wesin
            String oldKey = RedisConstant.USER_LOGIN_KEY_PREFIX + onlineToken;
            User offlineUser = (User) redisTemplate.opsForValue().get(oldKey);
            // 设置用户其他设备登录状态true，标识帐号在其他设备登录 2024/4/24 下午12:06 By Wesin
            offlineUser.setOtherDeviceOnline(true);
            redisTemplate.opsForValue().set(oldKey, offlineUser, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
        }
        // 登录用户token存入在线用户
        redisTemplate.opsForValue().set(onlineKey, token, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
        // 将用户信息存入redis中
        redisTemplate.opsForValue().set(loginKey, user, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
        redisTemplate.exec();
        // 封装结果将token传给前端
        LoginVo loginVo = new LoginVo();
        loginVo.setIsFirstLogin(user.getIsFirstLogin());
        loginVo.setToken(token);
        loginVo.setUserId(user.getId());
        loginVo.setAdminFlag(user.getAdminFlag());

        user.setLoginTime(new Date());
        userMapper.updateUser(user);
        loginRecord(user, platform);

        return loginVo;
    }


    @Override
    public Result userResetPassWord(UserForgetDto userForgetDto) {
        // 查看是否存在此用户
        User user = userMapper.getUserByPhoneNumber(userForgetDto.getMobile());
        if (user == null) {
            return Result.build(null, ResultCodeEnum.USER_NOT_EXIST);
        }
        // 查看缓存中是否有code
        // 定义RedisKey前缀
        String codePrefix = RedisConstant.USER_FORGET_KEY_PREFIX + userForgetDto.getMobile();
        Object code = redisTemplate.opsForValue().get(codePrefix);
        if (code == null) {
            return Result.build(null, ResultCodeEnum.CODE_OVERTIME);
        }
        if (!code.toString().equals(userForgetDto.getCode())) {
            return Result.build(null, ResultCodeEnum.PHONE_CODE_ERROR);
        }
        // code正确 修改密码
        UserForget userForget = BeanUtil.copyProperties(userForgetDto, UserForget.class);
        userForget.setUpdateTime(LocalDateTime.now());
        // 加密密码
        String encrypt = MD5.encrypt(userForget.getPassword());
        userForget.setPassword(encrypt);
        userMapper.updatePwdByMobile(userForget);
        redisTemplate.delete(codePrefix);

        // 存在登录用户就删除登录用户的token
        String onlineKey = RedisConstant.USER_ONLINE_KEY_PREFIX + user.getMobile();
        String onlineToken = (String) redisTemplate.opsForValue().get(onlineKey);
        if (StringUtils.isNotBlank(onlineToken)) {
            redisTemplate.delete(RedisConstant.USER_LOGIN_KEY_PREFIX + onlineToken);
            redisTemplate.delete(onlineKey);
        }

        return Result.ok();

    }

    @Override
    public Result userSignOut(String token) {
        User user = CommStatus.getUser(token);
        if (user != null) {
            String onlineKey = RedisConstant.USER_ONLINE_KEY_PREFIX + user.getMobile();
            redisTemplate.delete(onlineKey);
        }
        redisTemplate.delete(CommStatus.joinToken(token));
        return Result.ok();
    }


}
